import asyncio
import json
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, Optional

from loguru import logger

from core.command_manager import CommandRes


@dataclass
class ChatSession:
    seller_id: str
    item_id: Optional[str] = None
    seller_name: Optional[str] = None
    page: Optional[object] = None
    monitor_task: Optional[asyncio.Task] = None
    last_message_id: Optional[str] = None
    last_activity_at: datetime = field(default_factory=datetime.utcnow)


class ChatManager:
    """闲鱼聊天管理器（基于Playwright页面控制）"""

    def __init__(self, browser_manager, state_manager):
        self.browser_manager = browser_manager
        self.state = state_manager
        self.sessions: Dict[str, ChatSession] = {}
        self.tcp_client = None
        self._lock = asyncio.Lock()
        self._running = True

    def set_tcp_client(self, tcp_client):
        self.tcp_client = tcp_client

    def _make_session_key(self, seller_id: str, item_id: Optional[str]) -> str:
        item_part = item_id or ""
        return f"{seller_id}#{item_part}"

    async def open_chat(self, seller_id: str, item_id: Optional[str] = None, seller_name: Optional[str] = None):
        if not seller_id:
            raise ValueError("seller_id 不能为空")

        async with self._lock:
            key = self._make_session_key(seller_id, item_id)
            session = self.sessions.get(key)

            if session and session.page and not session.page.is_closed():
                logger.debug(f"[Chat] 会话已存在: {key}")
                session.last_activity_at = datetime.utcnow()
                return

            await self.browser_manager.start_browser()

            context = self.browser_manager.browser
            if not context:
                raise RuntimeError("浏览器未启动，无法打开聊天窗口")

            page = await context.new_page()
            await page.set_viewport_size({"width": 1280, "height": 800})

            url = f"https://www.goofish.com/im?sellerId={seller_id}"
            if item_id:
                url += f"&itemId={item_id}"

            logger.info(f"[Chat] 打开聊天页面: {url}")
            
            await page.goto(url, wait_until="networkidle", timeout=45000)

            await self._wait_chat_page_ready(page)

            if not session:
                session = ChatSession(seller_id=seller_id, item_id=item_id, seller_name=seller_name)
                self.sessions[key] = session

            # 监听 WebSocket 消息（用于实时获取聊天消息）
            # 注意：需要在 session 创建后注册，因为回调函数中引用了 session
            async def handle_websocket(ws):
                logger.debug(f"[Chat] WebSocket 连接: {ws.url}")
                try:
                    async for msg in ws:
                        if msg.type == "text":
                            try:
                                data = json.loads(msg.text)
                                # 检查是否是聊天消息
                                if self._is_chat_message(data, seller_id):
                                    logger.info(f"[Chat] 收到 WebSocket 聊天消息: {data}")
                                    await self._notify_websocket_message(session, data)
                            except json.JSONDecodeError:
                                pass
                            except Exception as e:
                                logger.debug(f"[Chat] WebSocket 消息处理异常: {e}")
                except Exception as e:
                    logger.debug(f"[Chat] WebSocket 监听结束: {e}")
            
            # 注册 WebSocket 监听器（在页面加载后）
            page.on("websocket", lambda ws: asyncio.create_task(handle_websocket(ws)))

            session.page = page
            session.seller_name = seller_name or session.seller_name
            session.last_activity_at = datetime.utcnow()

            # 如果有itemId，尝试获取商品详情以获取卖家头像和完整信息
            seller_avatar = None
            if item_id:
                try:
                    from modules.goofish_api import GoofishAPIClient
                    logger.debug(f"[Chat] 获取商品详情以提取卖家头像: item_id={item_id}")
                    api_client = GoofishAPIClient(self.state, self.browser_manager)
                    try:
                        detail_result = await api_client.get_item_detail(item_id)
                        if detail_result and isinstance(detail_result, dict):
                            data = detail_result.get('data', {})
                            seller_do = data.get('sellerDO', {})
                            if seller_do:
                                seller_avatar = seller_do.get('portraitUrl')
                                if seller_avatar:
                                    logger.debug(f"[Chat] 获取到卖家头像: {seller_avatar}")
                                # 如果没有seller_name，也从sellerDO获取
                                if not session.seller_name:
                                    session.seller_name = seller_do.get('nick') or seller_do.get('uniqueName')
                    finally:
                        await api_client.close()
                except Exception as e:
                    logger.debug(f"[Chat] 获取商品详情失败（不影响聊天功能）: {e}")

            if session.monitor_task and not session.monitor_task.done():
                session.monitor_task.cancel()

            session.monitor_task = asyncio.create_task(self._monitor_chat_session(key, session))

            return {
                "session_key": key,
                "seller_id": seller_id,
                "item_id": item_id,
                "seller_name": session.seller_name,
                "seller_avatar": seller_avatar
            }

    async def send_message(self, seller_id: str, item_id: Optional[str], message: str):
        if not message or not message.strip():
            raise ValueError("发送消息内容不能为空")

        await self.open_chat(seller_id, item_id)

        key = self._make_session_key(seller_id, item_id)
        session = self.sessions.get(key)
        if not session or not session.page or session.page.is_closed():
            raise RuntimeError("聊天页面未准备好")

        page = session.page
        await self._focus_message_input(page)

        input_selector_candidates = [
            'textarea',
            'textarea[placeholder*=消息]',
            '.message-input textarea',
            '.im-editor textarea'
        ]

        input_element = None
        for selector in input_selector_candidates:
            input_element = await page.query_selector(selector)
            if input_element:
                break

        if not input_element:
            raise RuntimeError("未找到消息输入框")

        await input_element.click()
        await input_element.fill("")
        await input_element.type(message, delay=50)

        send_button_candidates = [
            'button:has-text("发送")',
            '.send-btn',
            '.btn-send',
            'button[type="submit"]'
        ]

        sent = False
        for selector in send_button_candidates:
            btn = await page.query_selector(selector)
            if btn:
                await btn.click()
                sent = True
                break

        if not sent:
            await input_element.press('Enter')

        session.last_activity_at = datetime.utcnow()
        logger.info(f"[Chat] 已发送消息给 {seller_id}: {message[:50]}")

        # ✅ 发送成功后，上报到后端
        await self._report_sent_message(seller_id, item_id, message, session.seller_name)

        await asyncio.sleep(0.5)

        return {
            "session_key": key,
            "seller_id": seller_id,
            "item_id": item_id,
            "message": message
        }

    async def _report_sent_message(self, seller_id: str, item_id: Optional[str], message: str, seller_name: Optional[str] = None):
        """将发送的消息上报到后端"""
        if not self.tcp_client:
            logger.warning("[Chat] TCP客户端未初始化，无法上报发送消息")
            return
        
        try:
            # 生成临时消息ID
            temp_id = f"{int(datetime.now().timestamp() * 1000)}.TMP"
            
            await self.tcp_client.send({
                "event": "chat_message",
                "direction": "req",
                "data": {
                    "seller_id": seller_id,
                    "item_id": item_id,
                    "seller_name": seller_name or "未知卖家",
                    "message_id": temp_id,
                    "message_type": "text",
                    "direction": "send",  # ← 关键！标记为发送方向
                    "message_content": message,
                    "message_time": datetime.now().isoformat()
                }
            })
            logger.info(f"[Chat] ✅ 已上报发送消息: seller_id={seller_id}, message={message[:50]}")
        except Exception as e:
            logger.error(f"[Chat] ❌ 上报发送消息失败: {e}")

    async def _wait_chat_page_ready(self, page):
        selectors = [
            '.message-item',
            '.msg-item',
            '.im-message'
        ]
        for selector in selectors:
            try:
                await page.wait_for_selector(selector, timeout=15000)
                return
            except Exception:
                continue
        logger.warning("[Chat] 未检测到消息列表元素，继续监听")

    async def _focus_message_input(self, page):
        selectors = [
            'textarea',
            '.message-input textarea',
            '.im-editor textarea'
        ]
        for selector in selectors:
            try:
                await page.wait_for_selector(selector, timeout=5000)
                await page.focus(selector)
                return
            except Exception:
                continue
        raise RuntimeError("未找到消息输入框")

    async def _monitor_chat_session(self, session_key: str, session: ChatSession):
        logger.info(f"[Chat] 启动聊天监控: {session_key}")
        page = session.page
        while self._running and page and not page.is_closed():
            try:
                latest = await self._extract_latest_message(page)
                if latest and latest.get('message_id') and latest['message_id'] != session.last_message_id:
                    session.last_message_id = latest['message_id']
                    session.last_activity_at = datetime.utcnow()
                    logger.debug(f"[Chat] 新消息: {latest}")
                    await self._notify_new_message(session, latest)
            except asyncio.CancelledError:
                logger.info(f"[Chat] 监控任务取消: {session_key}")
                break
            except Exception as exc:
                logger.debug(f"[Chat] 监控异常 {session_key}: {exc}")
            await asyncio.sleep(2.0)

        logger.info(f"[Chat] 聊天监控结束: {session_key}")

    async def _extract_latest_message(self, page):
        script = """
        () => {
          const selectors = ['.message-item', '.msg-item', '.im-message'];
          let nodes = [];
          for (const sel of selectors) {
            const found = Array.from(document.querySelectorAll(sel));
            if (found && found.length) {
              nodes = found;
              break;
            }
          }
          if (!nodes.length) return null;
          const last = nodes[nodes.length - 1];
          const dataset = last.dataset || {};
          const sender = dataset.sender || (last.classList.contains('self') ? 'self' : (last.classList.contains('me') ? 'self' : 'other'));
          const messageId = dataset.id || last.getAttribute('data-id') || `${Date.now()}`;
          let text = '';
          const textNode = last.querySelector('.text, .content, p, span');
          if (textNode) {
            text = textNode.innerText || textNode.textContent || '';
          } else {
            text = last.innerText || last.textContent || '';
          }
          const timeNode = last.querySelector('.time, .timestamp');
          const timestamp = timeNode ? (timeNode.innerText || timeNode.textContent || '') : '';
          return {
            message_id: messageId,
            sender,
            content: text.trim(),
            timestamp
          };
        }
        """

        result = await page.evaluate(script)
        if result and result.get('content'):
            return result
        return None

    async def _notify_new_message(self, session: ChatSession, message: dict):
        if not self.tcp_client:
            return

        client_id = None
        if hasattr(self.state, 'get'):
            client_id = self.state.get('client_id') or self.state.get('clientId')

        payload = {
            "client_id": client_id,
            "seller_id": session.seller_id,
            "seller_name": session.seller_name,
            "item_id": session.item_id,
            "message_id": message.get('message_id'),
            "message_type": 'text',
            "direction": 'recv' if message.get('sender') != 'self' else 'send',
            "message_content": message.get('content'),
            "message_time": message.get('timestamp') or datetime.utcnow().isoformat()
        }

        await self.tcp_client.send(CommandRes.ChatMessage().Success(data=payload))

    def _is_chat_message(self, data: dict, seller_id: str) -> bool:
        """检查 WebSocket 消息是否是聊天消息"""
        if not isinstance(data, dict):
            return False
        
        # 检查常见的聊天消息标识
        # 注意：这里的格式需要根据实际闲鱼 WebSocket 消息格式调整
        if 'type' in data and data.get('type') in ['message', 'chat', 'im']:
            return True
        if 'action' in data and data.get('action') in ['send', 'receive']:
            return True
        if 'sellerId' in data and data.get('sellerId') == seller_id:
            return True
        if 'content' in data or 'message' in data or 'text' in data:
            return True
        
        return False

    async def _notify_websocket_message(self, session: ChatSession, data: dict):
        """处理 WebSocket 收到的聊天消息"""
        if not self.tcp_client:
            return

        client_id = None
        if hasattr(self.state, 'get'):
            client_id = self.state.get('client_id') or self.state.get('clientId')

        # 提取消息内容（格式需要根据实际调整）
        message_content = data.get('content') or data.get('message') or data.get('text') or ''
        message_id = data.get('message_id') or data.get('id') or str(datetime.utcnow().timestamp())
        direction = 'recv' if data.get('from') != 'self' else 'send'

        payload = {
            "client_id": client_id,
            "seller_id": session.seller_id,
            "seller_name": session.seller_name,
            "item_id": session.item_id,
            "message_id": message_id,
            "message_type": 'text',
            "direction": direction,
            "message_content": message_content,
            "message_time": data.get('timestamp') or datetime.utcnow().isoformat()
        }

        await self.tcp_client.send(CommandRes.ChatMessage().Success(data=payload))

    async def shutdown(self):
        self._running = False
        for session in list(self.sessions.values()):
            if session.monitor_task and not session.monitor_task.done():
                session.monitor_task.cancel()
            if session.page and not session.page.is_closed():
                try:
                    await session.page.close()
                except Exception:
                    pass
        self.sessions.clear()

